import logging
import os
import re
import time
from urllib.parse import urljoin

import requests


# 这个类实际上是把m3u8转成iptv m3u
class M3uGenTools:
    # 存在一些问题，在__init__里就读取json文件的做法似乎不太好
    def __init__(self, chanel_base_info: list[dict], headers: dict = None, channel_logo_json_path: str = None):
        """

        param chanel_base_info:list of dict
        """
        # log config
        logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
        self.logger = logging.getLogger(__name__)

        self.session = requests.Session()
        if headers is not None:
            self.headers = headers
            self.session.headers = headers
        else:
            self.headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                              "Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.36"}
            self.session.headers = self.headers

        import json
        self.chanel_base_info = chanel_base_info
        self.channel_logo_info = []
        # abs_dirname = os.path.dirname(os.path.abspath(__file__))
        # self.logger.debug(abs_dirname)
        if channel_logo_json_path is not None:
            try:
                with open(channel_logo_json_path, 'r', encoding='utf-8') as f:
                    self.channel_logo_info = json.load(f)
            except FileNotFoundError:
                self.logger.error(
                    '%s not found, please create %s first' % (channel_logo_json_path, channel_logo_json_path))
                exit(1)

    def get_real_url(self, url_list: list):
        """
        返回一个重定向后的url的列表
        return a list of real url

        :rtype: list
        """
        real_url = []
        for item in url_list:
            try:
                result = self.session.get(item, headers=self.headers, timeout=5, stream=True, allow_redirects=True)
                if result.url != item:
                    real_url.append(result.url)
                else:
                    real_url.append(item)
            except Exception as exception:
                self.logger.debug(exception)
                return []

            time.sleep(0.2)

        return real_url

    def get_m3u8_video_url(self, url_list):
        """

        返回一个m3u8链接里的第一段视频的url
        return a list of first video url in m3u8 url
        # :type url_list: list
        """
        url_data = []
        content_pattern = re.compile(r'^[^#\s].*', re.M)
        url_pattern = re.compile(r'^(http|https)://.*')
        m3u8_content = self.session.get(url_list, timeout=5).text
        content = re.findall(content_pattern, m3u8_content)[0]
        isurl = re.findall(url_pattern, content)
        if len(isurl) == 0:
            target_url = str(urljoin(url_list, content).strip())
        else:
            target_url = content
        self.logger.info("target_url: %s" % target_url)
        time.sleep(0.2)
        url_data.append(target_url)
        if len(url_data) == 0:
            raise Exception('url_data is empty')
        return url_data[0]

    def speedtest(self, url_list):
        """
        使用requests 的速度测试方法
        use requests speed test method
        """
        import urllib.parse
        data = []
        for item in url_list:
            try:
                self.logger.debug('item: %s' % item)
                result = self.get_m3u8_video_url(item)
                if urllib.parse.urlparse(result).path.endswith('.m3u8'):
                    result = self.get_m3u8_video_url(result)
                result_dict = self.download_speed(result, timeout=20)
                # 将测速的视频段url替换成原始的m3u8 url
                result_dict[0] = item
                data.append(result_dict)
            except Exception as exception:
                self.logger.debug('链接失败: %s\n%s' % (item, exception))
        if len(data) == 0:
            raise Exception('list: data is empty')
        self.sort_by_second(data)
        return data

    def get_channel_logo_info(self, tvg_name: str):
        """

        :param tvg_name:
        :return:
        """
        for item in self.channel_logo_info:
            if item['tvg_name'] == tvg_name:
                return [item['category'], item['logo_url']]
        return []

    def get_m3u_info(self):
        """

        :return: m3u_info: list of dict
        """
        m3u_info = []
        for item in self.chanel_base_info:
            channel_name = item['name']
            channel_url = item['url']
            # real_channel_url = self.get_real_url([channel_url])
            # if len(real_channel_url) == 0:
            #     continue
            # else:
            #     channel_url = real_channel_url[0]
            tvg_name = self.path_tvg_name(channel_name)
            channel_logo_info = self.get_channel_logo_info(tvg_name)
            if len(channel_logo_info) == 0:
                self.logger.debug('channel_logo_info is empty')
                channel_logo_url = ''
                channel_category = ''
            else:
                channel_logo_url = channel_logo_info[1]
                channel_category = channel_logo_info[0]
            m3u_info.append({'channel_name': channel_name, 'tvg_name': tvg_name, 'channel_url': channel_url,
                             'logo_url': channel_logo_url, 'category': channel_category})
        return m3u_info

    def write_m3u(self, m3u_path: str = 'result' + os.sep + 'all.m3u'):
        """

        :param m3u_path:
        :return:
        """
        m3u_info = self.get_m3u_info()
        file = '#EXTM3U\n'
        for item in m3u_info:
            file += self.m3u_item_generator(item)
        m3u_file = file
        self.mkdir('result')
        with open(m3u_path, 'w', encoding='utf-8') as f:
            f.write(m3u_file)

    @staticmethod
    def sort_by_second(tuples):
        return sorted(tuples, key=lambda tup: tup[1], reverse=True)

    @staticmethod
    def download_speed(url, timeout=5):
        import urllib.request
        try:
            req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
            start = time.time()
            res = urllib.request.urlopen(req, timeout=timeout)
            size = len(res.read())
            end = time.time()
        except Exception as e:
            print(e)
        else:
            return [url,
                    round(size / (end - start) / 1024, 2)]

    @staticmethod
    def mkdir(path):
        import os
        path = path.strip()
        path = path.rstrip(os.sep)
        is_exists = os.path.exists(path)
        if not is_exists:
            os.makedirs(path)
            return True
        else:
            return False

    @staticmethod
    def m3u_item_generator(channel_info: dict):
        """

        :param channel_info:
        :return:
        """
        m3u_line = '#EXTINF:-1 tvg-logo="%s" tvg-name="%s" group-title="%s",%s\n%s\n' % (
            channel_info['logo_url'], channel_info['tvg_name'], channel_info['category'], channel_info['channel_name'],
            channel_info['channel_url'],)
        return m3u_line

    @staticmethod
    def path_tvg_name(channel_name: str):
        """

        :param channel_name:
        :return:
        """
        tvg_name = channel_name.split(' ')[0]
        return tvg_name
